#pragma once

#include "iterator_tag.hpp"
#include "compile_const.hpp"

namespace zuck
{
	template<typename ...Args>
	inline size_t constexpr args_size = (sizeof(Args) + ...);

	inline int constexpr small_object_num_ptrs = 6 + 16 / sizeof(void*);

	template<typename>
	inline bool constexpr always_false = false;

	template<typename From, typename To>
	inline bool constexpr is_safe_cast = static_cast<bool>(sizeof(From) <= sizeof(To));

	template<typename Type1, typename Type2>
	inline bool constexpr is_same_v = false;

	template<typename T>
	inline bool constexpr is_same_v<T, T> = true;

	template<typename T>
	inline bool constexpr is_pointer_v = false;

	template<typename T>
	inline bool constexpr is_pointer_v<T*> = true;

	template<typename T>
	inline bool constexpr is_array_v = false;

	template<typename T>
	inline bool constexpr is_array_v<T[]> = true;

	template<typename T, ::zuck::size_t _Size>
	inline bool constexpr is_array_v<T[_Size]> = true;

	template<typename T>
	inline bool constexpr is_const_v = false;

	template<typename T>
	inline bool constexpr is_const_v<T const> = true;

	template<typename T>
	inline bool constexpr is_volatile_v = false;

	template<typename T>
	inline bool constexpr is_volatile_v<T volatile> = true;

	template<typename T>
	inline bool constexpr is_void_v = false;

	template<>
	inline bool constexpr is_void_v<void> = true;

	template<typename T>
	inline bool constexpr is_ref_v = false;

	template<typename T>
	inline bool constexpr is_ref_v<T&> = true;

	template<typename T>
	inline bool constexpr is_ref_v<T&&> = true;

	template<typename T>
	inline bool constexpr is_lvalue_ref_v = false;

	template<typename T>
	inline bool constexpr is_lvalue_ref_v<T&> = true;

	template<typename T>
	inline bool constexpr is_rvalue_ref_v = false;

	template<typename T>
	inline bool constexpr is_rvalue_ref_v<T&&> = true;

	template<typename T>
	inline bool constexpr is_function_v = !is_const_v<T> && !is_ref_v<T>;

	template<typename T>
	inline bool constexpr is_object_v = is_const_v<const T> && !is_void_v<T>;

	template<typename Iterator>
	inline bool constexpr is_forward_iterator_v =
		is_same_v<typename Iterator::iterator_category, forward_iterator_tag>;

	template<typename T>
	inline bool constexpr is_forward_iterator_v<T*> = true;

	template<typename Iterator>
	inline bool constexpr is_bidirectional_iterator_v =
		is_same_v<typename Iterator::iterator_category, bidirectional_iterator_tag>;

	template<typename T>
	inline bool constexpr is_bidirectional_iterator_v<T*> = true;

	template<typename Iterator>
	inline bool constexpr is_random_access_iterator_v =
		is_same_v<typename Iterator::iterator_category, random_access_iterator_tag>;

	template<typename T>
	inline bool constexpr is_random_access_iterator_v<T*> = true;

	template<typename Iterator>
	inline bool constexpr is_input_iterator_v = is_forward_iterator_v<Iterator> ||
		is_bidirectional_iterator_v<Iterator> ||
		is_random_access_iterator_v<Iterator>;

	template<typename Iterator>
	inline bool constexpr is_output_iterator_v = is_same_v<typename Iterator::iterator_cateory,
		output_iterator_tag>;

	template<typename T, typename = void>
	inline bool constexpr is_iterator_v = false;

	template<typename T>
	inline bool constexpr is_iterator_v<T*, void_t<T*>> = true;

	template<typename Iterator>
	inline bool constexpr is_iterator_v<Iterator, void_t<typename Iterator::iterator_category>> = true;


	inline ::zuck::size_t constexpr default_align_v = __STDCPP_DEFAULT_NEW_ALIGNMENT__;

	template<typename T>
	inline ::zuck::size_t constexpr _T_align_v = alignof(T);

	template<typename T>
	inline ::zuck::size_t constexpr new_align_v = _T_align_v<T> < default_align_v ? default_align_v : _T_align_v<T>;


	template<typename T, T val>
	class integral_constant {
	public:
		using value_type = T;
		using type = integral_constant<T, val>;

		static T constexpr value = val;
	public:
		constexpr operator T()const
		{
			return value;
		}

		T constexpr operator()()const noexcept
		{
			return value;
		}
	};

	template<bool value>
	using bool_constant = integral_constant<bool, value>;

	using false_type = bool_constant<false>;
	using true_type = bool_constant<true>;

	template<typename Ty1, typename Ty2>
	class is_same : public bool_constant<is_same_v<Ty1, Ty2>> {};

	template<typename T>
	class is_pointer : public bool_constant<is_pointer_v<T>> {};

	template<typename T>
	class is_const : public bool_constant<is_const_v<T>> {};

	template<typename T>
	class is_void : public bool_constant<is_void_v<T>> {};

	template<typename T>
	class is_reference : public bool_constant<is_ref_v<T>> {};

	template<typename T>
	class is_function : public bool_constant<is_function_v<T>> {};

	template<typename T>
	class is_object : public bool_constant<is_object_v<T>> {};

namespace __detail
{
	template<bool __same, typename First, typename ...Rest>
	class __disjunction {// handle the first type
	public:
		using type = First;
	};

	template<typename First, typename Next, typename ...Rest>
	class __disjunction<false, First, Next, Rest...> {// the first trait is false, handle  the next trait
	public:
		using type = typename __disjunction<Next::value, Next, Rest...>::type;
	};
}

	template<typename ...Tratis>
	class disjunction : zuck::false_type {};

	template<typename First, typename ...Rest>
	class disjunction<First, Rest...> : public __detail::__disjunction<First::value, First, Rest...>::type {};

	template<typename ...Traits>
	bool constexpr disjunction_v = disjunction<Traits...>::value;

	template<typename T, typename ...Types>
	bool constexpr is_any_of = disjunction_v<zuck::is_same<T, Types>...>;

	template<typename T>
	inline bool constexpr is_integral_v = is_any_of<T,
		char, short, int, long, long, long long,
		unsigned char, unsigned short, unsigned int,
		unsigned long, unsigned long long, char16_t, char32_t
#if __cplusplus__ >= 202002
#if __GNUC__// int128 type
		, __int128, char8_t>;
#else
		, char8_t>;
#endif
#else
#if __GNUC__// int128 type
		, __int128>;
#else
		>;
#endif
#endif

	template<typename T>
	inline bool constexpr is_signed_v = is_any_of<T, char, short, int, long, long, long, char16_t, char32_t>;

	template<typename T>
	inline bool constexpr is_unsigned_v = is_any_of<T,
		unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long>;

	template<typename T>
	inline bool constexpr is_floating_v = is_any_of<T, float, double
#if __GNUC__// float128 type
		, long double>;
#else
	> ;
#endif

	template<typename T>
	bool constexpr is_inside_type_v = is_integral_v<T> || is_floating_v<T> ||
		is_pointer_v<T> || is_void_v<T>;

	template<typename T>
	inline bool constexpr is_base_type_v = is_integral_v<T> || is_pointer_v<T> || is_floating_v<T>;

	template<typename T>
	inline bool constexpr is_class_v = !is_base_type_v<T>;

	template<typename T>
	inline bool constexpr is_empty_v = __is_empty(T);

	template<typename T>
	class is_integral : public bool_constant<is_integral_v<T>> {};

	template<typename T>
	class is_floating : public bool_constant<is_floating_v<T>> {};

	template<typename T>
	class __static_array_max_size
	{
	public:
		[[nodiscard]] inline static zuck::size_t constexpr div()
		{
			if constexpr (zuck::is_inside_type_v<T>) {
				return 0x20ull;
			}
			return sizeof(T);
		}
	};

	template<typename T>
	inline auto constexpr max_static_array_v = ((zuck::max_env_size / sizeof(T)) >>
		__static_array_max_size<T>::div());

	template<typename T>
	class add_lvalue_reference {
	public:
		using type = T&;
	};
	template<typename T>
	class add_lvalue_reference<T&> {
	public:
		using type = T&;
	};

	template<typename T>
	class add_rvalue_reference {
	public:
		using type = T;
	};
	template<typename T>
	class add_rvalue_reference<T&&> {
	public:
		using type = T&&;
	};

	template<typename T> 
	class add_const {
	public:
		using type = const T;
	};
	template<typename T> 
	class add_const<const T> {
	public:
		using type = const T;
	};

	template<typename T>
	class add_volatile {
	public:
		using type = volatile T;
	};
	template<typename T>
	class add_volatile<volatile T> {
	public:
		using type = volatile T;
	};

	template<typename T>
	class add_cv {
	public:
		using type = const volatile T;
	};
	template<typename T>
	class add_cv<const T> {
	public:
		using type = const volatile T;
	};
	template<typename T>
	class add_cv<volatile T> {
	public:
		using type = const volatile T;
	};


	template<typename T> 
	class add_pointer {
	public:
		using type = T*;
	};
	template<typename T> 
	class add_pointer<T*> {
	public:
		using type = T*;
	};

	template<typename Integer_T>
	class make_signed {
	public:
		using type = signed int;
	};

	template<>
	class make_signed<unsigned char> {
	public:
		using type = signed char;

		inline static ::zuck::size_t constexpr size = 1;
	};

	template<>
	class make_signed<char> {
	public:
		using type = signed char;

		inline static ::zuck::size_t constexpr size = 1;
	};

	template<>
	class make_signed<unsigned short> {
	public:
		using type = signed short;

		inline static ::zuck::size_t constexpr size = 2;
	};

	template<>
	class make_signed<short> {
	public:
		using type = signed short;

		inline static ::zuck::size_t constexpr size = 2;
	};

	template<>
	class make_signed<int> {
	public:
		using type = signed int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_signed<unsigned int> {
	public:
		using type = signed int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_signed<unsigned long> {
	public:
		using type = signed long;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_signed<long> {
	public:
		using type = signed long;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_signed<unsigned long long> {
	public:
		using type = signed long long;

		inline static ::zuck::size_t constexpr size = 8;
	};

	template<>
	class make_signed<long long> {
	public:
		using type = signed long long;

		inline static ::zuck::size_t constexpr size = 8;
	};

 
	template<typename Integer_T>
	class make_unsigned {
	public:
		using type = unsigned int;
	};

	template<>
	class make_unsigned<char> {
	public:
		using type = unsigned char;

		inline static ::zuck::size_t constexpr size = 1;
	};

	template<>
	class make_unsigned<unsigned char> {
	public:
		using type = unsigned char;

		inline static ::zuck::size_t constexpr size = 1;
	};

	template<>
	class make_unsigned<short> {
	public:
		using type = unsigned short;

		inline static ::zuck::size_t constexpr size = 2;
	};

	template<>
	class make_unsigned<unsigned short> {
	public:
		using type = unsigned short;

		inline static ::zuck::size_t constexpr size = 2;
	};

	template<>
	class make_unsigned<int> {
	public:
		using type = unsigned int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_unsigned<unsigned int> {
	public:
		using type = unsigned int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_unsigned<long> {
		using type = unsigned int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_unsigned<unsigned long> {
	public:
		using type = unsigned int;

		inline static ::zuck::size_t constexpr size = 4;
	};

	template<>
	class make_unsigned<long long> {
	public:
		using type = unsigned long long;

		inline static ::zuck::size_t constexpr size = 8;
	};
	
	template<>
	class make_unsigned<unsigned long long> {
	public:
		using type = unsigned long long;

		inline static ::zuck::size_t constexpr size = 8;
	};


	template<typename T> 
	class remove_reference {
	public:
		using type			 = T;
		using const_ref_type = const T&;
	};
	template<typename T> 
	class remove_reference<T&> {
	public:
		using type			 = T;
		using const_ref_type = const T&;
	};
	template<typename T> 
	class remove_reference<T&&> {
	public:
		using type			 = T;
		using const_ref_type = const T&;
	};


	template<typename T>
	class remove_cv {
	public:
		using type = T;
	};
	template<typename T>
	class remove_cv<T const> {
	public:
		using type = T;
	};
	template<typename T>
	class remove_cv<T const*> {
	public:
		using type = T*;
	};
	template<typename T>
	class remove_cv<T volatile> {
	public:
		using type = T;
	};
	template<typename T>
	class remove_cv<T const volatile> {
	public:
		using type = T;
	};
	template<typename T>
	class remove_cv<T const volatile*> {
	public:
		using type = T;
	};

	template<typename T> 
	class remove_pointer {
	public:
		using type = T;
	};
	template<typename T>
	class remove_pointer<T*> {
	public:
		using type = T;
	};


	template<typename T, typename = void>
	class iterator_traits ;
	template<typename Iterator>
	class iterator_traits<Iterator, void_t<typename Iterator::iterator_category>> {
	public:
		using value_type        = typename Iterator::value_type;
		using pointer			= typename Iterator::pointer;
		using const_pointer		= typename Iterator::const_pointer;
		using difference_type   = typename Iterator::difference_type;
		using iterator_category = typename Iterator::iterator_category;
	};

	template<typename T>
	class iterator_traits<T*, void_t<::zuck::random_access_iterator_tag>> {
	public:
		using value_type        = T;
		using pointer           = T*;
		using const_pointer		= const T*;
		using difference_type   = zuck::ptrdiff_t;
        using iterator_category = zuck::random_access_iterator_tag;
	};


	template<typename T = void>
	class pointer_traits;

	template<typename T>
	class pointer_traits<T*> 
		: public iterator_traits<T*, void_t<zuck::random_access_iterator_tag>>
	{};


	template<bool expr, typename T = void>
	class enable_if {};

	template<typename T> 
	class enable_if<true, T> {
	public:
		using type = T;
	};

	template<bool expr, typename T1, typename T2> 
	class conditional {};
	template<typename T1, typename T2>
	class conditional<true, T1, T2> {
	public:
		using type = T1;
	};
	template<typename T1, typename T2>
	class conditional<false, T1, T2> {
	public:
		using type = T2;
	};

	template<typename T>
	class is_nothrow_constructible : bool_constant<__is_nothrow_constructible(T, T)> {};

	template<typename T>
	using add_const_t = typename add_const<T>::type;

	template<typename Integer_T>
	using make_unsigned_t = typename make_unsigned<Integer_T>::type;

	template<typename T>
	using iter_traits_t = typename iterator_traits<T>::value_type;

	template<typename T>
	using iter_tag_t = typename iterator_traits<T>::iterator_category;

	template<typename T>
	using iter_diff_t = typename iterator_traits<T>::difference_type;

	template<typename T>
	using remove_ref_t = typename remove_reference<T>::type;

	template<typename T>
	using remove_ptr_t = typename remove_pointer<T>::type;

	template<typename T>
	using remove_cv_t = typename remove_cv<T>::type;

	template<typename T>
	using const_t = typename add_const<T>::type;

	template<bool flag, typename T>
	using enable_if_t = typename enable_if<flag, T>::type;

	template<bool flg, typename T1, typename T2>
	using conditional_t = typename conditional<flg, T1, T2>::type;

	template<typename T>
	[[nodiscard]] inline constexpr T&& forward(remove_ref_t<T>& _Arg)
	{
		return static_cast<T&&>(_Arg);
	}

	template<typename T>
	[[nodiscard]] inline constexpr T&& forward(remove_ref_t<T>&& _Arg)
	{
		static_assert(!zuck::is_lvalue_ref_v<T>, "T could not be lvalue reference");

		return static_cast<T&&>(_Arg);
	}

	template<typename T>
	[[nodiscard]] inline constexpr remove_ref_t<T>&& move(T&& _Arg)
	{
		return static_cast<remove_ref_t<T>&&>(_Arg);
	}
}

